Last execution time: 20/01/2025 05:45:11
Get data
Products type filter
explore_types = ['frutas', 'lacteos', 'verduras', 'embutidos', 'panaderia', 'desayuno', 'congelados', 'abarrotes',
'aves', 'carnes', 'pescados']Data table
path = Path('../../output')
csv_files = L(path.glob('*.csv')).filter(lambda o: os.stat(o).st_size>0)
pat_store = re.compile('(.+)\_\d+')
pat_date = re.compile('.+\_(\d+)')
df = (
pd.concat([pd.read_csv(o).assign(store=pat_store.match(o.stem)[1], date=pat_date.match(o.stem)[1])
for o in csv_files], ignore_index=True)
.pipe(lambda d: d.assign(
name=d.name.str.lower()+' ('+d.store+')',
sku=d.id.where(d.sku.isna(), d.sku).astype(int),
date=pd.to_datetime(d.date)
))
.drop('id', axis=1)
.loc[lambda d: d.category.str.contains('|'.join(explore_types))]
# Filter products with recent data
# .loc[lambda d: d.name.isin(d.groupby('name').date.max().loc[ge(datetime.now()-timedelta(days=30))].index)]
# Filter empty prices
.loc[lambda d: d.price>0]
)
print(df.shape)
df.sample(3)(1220936, 8)
| sku | name | brand | category | uri | price | store | date | |
|---|---|---|---|---|---|---|---|---|
| 2043194 | 10723763 | mezcla láctea ideal amanecer 6 pack lata 390g ... | IDEAL AMANECER | https://www.plazavea.com.pe/lacteos-y-huevos | https://www.plazavea.com.pe/mezcla-lactea-idea... | 19.5 | plaza_vea | 2023-05-08 |
| 2511516 | 71447 | pistachos bell's táper 150g (plaza_vea) | BELL'S | https://www.plazavea.com.pe/abarrotes | https://www.plazavea.com.pe/pistachos-bells-ta... | 17.9 | plaza_vea | 2025-01-06 |
| 127880 | 20015 | salsa de ají criollo walibi doypack 85g (plaza... | WALIBI | https://www.plazavea.com.pe/abarrotes | NaN | 3.6 | plaza_vea | 2023-01-26 |
Top changes (ratio)
Code
top_changes = (df
# Use last 30 days of data to compare prices
.loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
.sort_values('date')
# Get percentage change
.assign(change=lambda d: d
.groupby(['store','sku'], as_index=False)
.price.transform(lambda d: (d-d.shift())/d.shift())
)
.groupby(['store','sku'], as_index=False)
.agg({'price':'last', 'change':'mean', 'date':'last'})
.rename({'price':'last_price', 'date':'last_date'}, axis=1)
.dropna()
.loc[lambda d: d.last_date==d.last_date.max()]
.loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes.head(3)| store | sku | last_price | change | last_date | |
|---|---|---|---|---|---|
| 1279 | plaza_vea | 10808 | 23.96 | 0.519027 | 2025-01-20 |
| 2725 | plaza_vea | 10054239 | 4.39 | 0.331818 | 2025-01-20 |
| 4386 | plaza_vea | 11102758 | 4.99 | 0.271053 | 2025-01-20 |
Code
def plot_changes(df_changes, title):
selection = alt.selection_point(fields=['name'], bind='legend')
dff = df_changes.drop('change', axis=1).merge(df, on=['store','sku'])
return (dff
.pipe(alt.Chart)
.mark_line(point=True)
.encode(
x='date',
y='price',
color=alt.Color('name').scale(domain=sorted(dff.name.unique().tolist())),
tooltip=['name','price','last_price']
)
.add_params(selection)
.transform_filter(selection)
.interactive()
.properties(width=650, title=title)
.configure_legend(orient='top', columns=3)
)Code
top_changes.head(10).pipe(plot_changes, 'Top changes')Code
(top_changes
.sort_values('change')
.head(10)
.pipe(plot_changes, 'Top drops')
)Code
(top_changes
.sort_values('change')
.tail(10)
.pipe(plot_changes, 'Top increases')
)Top changes (absolute values)
Code
top_changes_abs = (df
# Use last 30 days of data to compare prices
.loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
.sort_values('date')
# Get percentage change
.assign(change=lambda d: d
.groupby(['store','sku'], as_index=False)
.price.transform(lambda d: (d-d.shift()).iloc[-1])
)
.groupby(['store','sku'], as_index=False)
.agg({'price':'last', 'change':'mean', 'date':'last'})
.rename({'price':'last_price', 'date':'last_date'}, axis=1)
.dropna()
.loc[lambda d: d.last_date==d.last_date.max()]
.loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes_abs.head(3)| store | sku | last_price | change | last_date | |
|---|---|---|---|---|---|
| 2424 | plaza_vea | 10012680 | 112.8 | -44.7 | 2025-01-20 |
| 2658 | plaza_vea | 10037893 | 225.0 | -32.0 | 2025-01-20 |
| 3867 | plaza_vea | 10734126 | 162.0 | -27.0 | 2025-01-20 |
Code
top_changes_abs.head(10).pipe(plot_changes, 'Top changes')Code
(top_changes_abs
.sort_values('change')
.head(10)
.pipe(plot_changes, 'Top drops')
)Code
(top_changes_abs
.sort_values('change')
.tail(10)
.pipe(plot_changes, 'Top increases')
)Search specific products
Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Pollo')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Palta')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)